#pragma once
#include "../source/message.hpp"
#include "../source/net.hpp"
#include "Requestor.hpp"
#include <set>


namespace ns_client_registry
{
    class Provider
    {
    public:
        using ptr = std::shared_ptr<Provider>;
        bool RegistryMethod(const BaseConnection::ptr& conn,const Address& host,const std::string& method)
        {
            auto msg_req = MessageFactory::Createmsg<ServiceRequest>();
            msg_req->Setid(ns_uuid::UUIDUtil::uuid());
            msg_req->SetMethod(method);
            msg_req->SetMtype(Mtype::SERVICE_REQ);
            msg_req->SetOtype(ServiceOptype::SERVICE_REGISTRY);
            msg_req->SetHost(host);

            BaseMessage::ptr msg_rsp;
            bool ret = _requestor->Send(conn,msg_req,msg_rsp);
            if(ret == false)
            {
                LOG(ERROR,"requestor发送请求失败!!!");
                return false;
            }
            auto service_rsp = std::dynamic_pointer_cast<ServiceResponse>(msg_rsp);
            if(service_rsp.get() == nullptr)
            {
                LOG(ERROR,"msg_rsp响应向下转换失败!!!");
                return false;
            }
            if(service_rsp->GetRcode() != Rcode::RCODE_OK)
            {
                LOG(ERROR,"服务注册失败，原因：%s",ns_fields::errReason(service_rsp->GetRcode()).c_str());
                return false;
            }
            return true;
        }
    private:
        ns_Req::Requestor::ptr _requestor;
    };

    class MethodHosts
    {
    public:
        using ptr = std::shared_ptr<MethodHosts>;
        MethodHosts()
        :_index(0)
        {}
        MethodHosts(const std::vector<Address> hosts)
        :_methodhosts(hosts)
        ,_index(0)
        {}

        //用到的频率低
        void AppendHost(const Address& host)
        {   //服务上线时调用
            std::unique_lock<std::mutex> _mutex;
            _methodhosts.push_back(host);
        }
        void DelHost(const Address& host)
        {   //服务下线时调用
            std::unique_lock<std::mutex> _mutex;
            for(auto it = _methodhosts.begin();it!=_methodhosts.end();it++)
            {
                if(*it == host)
                    _methodhosts.erase(it);
            }
        }

        //用到的频率高
        Address ChooseHost()
        {
            std::unique_lock<std::mutex> _mutex;
            size_t pos = _index++ % _methodhosts.size();
            return _methodhosts[pos];
        }
        bool empty()
        {
            return _methodhosts.empty();
        }
    private:
        std::mutex _mutex;
        size_t _index;
        std::vector<Address> _methodhosts;
    };
    class Discoverer
    {
    public:
        using ptr = std::shared_ptr<Discoverer>;
        Discoverer(const ns_Req::Requestor::ptr requestor)
        :_requestor(requestor)
        {}
        //服务发现
        bool ServiceDiscover(const BaseConnection::ptr& conn,std::string method,Address& host)
        {
            //1.先查看_hashhosts(保管者)里有无要发现的方法所对应的主机组信息
            auto it = _hashhosts.find(method);
            if(it != _hashhosts.end())
            {
                //还要判断一层的原因：有可能服务发现时，刚好有服务下线，那它对应的主机可能变为0，所以不能访问一个空的MethodHosts
                if(!_hashhosts[method]->empty())
                {
                    //不为空，服务发现成功，主机地址负载均衡式返回给host
                    host = _hashhosts[method]->ChooseHost();
                    return true;
                }
            }
            //2.如果_hashhosts没有此方法所对应的主机组信息，主动构建，进行获取
            auto msg_req = MessageFactory::Createmsg<ServiceRequest>();
            msg_req->Setid(ns_uuid::UUIDUtil::uuid());
            msg_req->SetMethod(method);
            msg_req->SetMtype(ns_fields::Mtype::SERVICE_REQ);
            msg_req->SetOtype(ServiceOptype::SERVICE_DISCOVER);

            BaseMessage::ptr msg_rsp;
            bool ret = _requestor->Send(conn,msg_req,msg_rsp);
            if(ret == false)
            {
                LOG(ERROR,"服务发现请求失败");
                return false;
            }
            auto service_rsp = std::dynamic_pointer_cast<ServiceResponse>(msg_rsp);
            if(service_rsp.get() == nullptr)
            {
                LOG(ERROR,"msg_rsp响应向下转换失败!!!");
                return false;
            }
            if(service_rsp->GetRcode() != Rcode::RCODE_OK)
            {
                LOG(ERROR,"服务发现失败，原因：%s",ns_fields::errReason(service_rsp->GetRcode()).c_str());
                return false;
            }
            std::unique_lock<std::mutex> lock(_mutex);
            auto method_host = std::make_shared<MethodHosts>(service_rsp->GetHosts());
            if(method_host->empty())
            {
                LOG(ERROR,"服务发现失败，原因：%s",ns_fields::errReason(service_rsp->GetRcode()).c_str());
                return false;
            }
            host = method_host->ChooseHost();
            //防止已经存在的情况，如果已经有值，这种可以覆盖字段，insert会插入失败
            _hashhosts[method] = method_host;
            return true;
        }
        //给dispacher模块服务上线下线请求的处理回调
        void OnServiceRequest(const BaseConnection::ptr& conn,const ServiceRequest::ptr& msg)
        {
            //如果不是上线/下线请求，就不做处理
            if(msg->GetOtype() != ServiceOptype::SERVICE_ONLINE || msg->GetOtype() != ServiceOptype::SERVICE_OFFLINE)
                return;
            //如果是上线：往_hashhosts里新加对应方法的一个主机
            if(msg->GetOtype() == ServiceOptype::SERVICE_ONLINE)
            {
                auto host = msg->GetHost();
                auto it = _hashhosts.find(msg->GetMethod());
                if(it == _hashhosts.end())
                {
                    //构建一个新的映射组，加入_hashhosts
                    auto method_host = std::make_shared<MethodHosts>();
                    method_host->AppendHost(msg->GetHost());
                    _hashhosts[msg->GetMethod()] = method_host;
                }
                else
                {
                    it->second->AppendHost(msg->GetHost());
                }
            }
            //如果是下线：往_hashhosts里减少对应方法的一个主机
            if(msg->GetOtype() == ServiceOptype::SERVICE_OFFLINE)
            {
                auto host = msg->GetHost();
                auto it = _hashhosts.find(msg->GetMethod());
                if(it == _hashhosts.end())
                    return;
                else
                {
                    it->second->DelHost(msg->GetHost());
                }
            }
            //不需要进行发送响应了，因为没有必要
        }
    private:
        std::mutex _mutex;
        std::unordered_map<std::string,MethodHosts::ptr> _hashhosts;
        ns_Req::Requestor::ptr _requestor;
    };
}